import { deepStrictEqual, strictEqual } from 'assert';
import { Mock } from 'lite-ts-mock';
import { ImagePSD, TreePSD } from 'psd';

import { ConverterBase } from './base';
import { Layers, Node, Sprite, UITransform, Vec2, Vec3 } from './cc';
import { AssertServiceBase, ConverterFactoryBase, FileFactoryBase, IConverter, IFile } from '../../contract';
import { enum_ } from '../../model';

class Self extends ConverterBase {
    public async convert() { }
}

describe('extensions/psd2prefab/src/scene/service/converter/base.ts', () => {
    describe('.addChildren(node: any)[protected]', () => {
        it('ok', async () => {
            const mockConverterFactory = new Mock<ConverterFactoryBase>();
            const mockTreePSD = new Mock<TreePSD>();
            const self = new Self(null as any, mockConverterFactory.actual, null as any, mockTreePSD.actual);

            const mockChildTreePSD = new Mock<TreePSD>();
            mockTreePSD.expectReturn(
                r => r.children(),
                [mockChildTreePSD.actual]
            );

            mockChildTreePSD.expectReturn(
                r => r.get(enum_.Psd.name),
                'a'
            );

            const mockConverter = new Mock<IConverter>();
            mockConverterFactory.expectReturn(
                r => r.build(mockChildTreePSD.actual),
                mockConverter.actual
            );

            const child = {
                parent: null
            };
            mockConverter.expectReturn(
                r => r.convert(),
                child
            );

            const fn = Reflect.get(self, 'addChildren').bind(self) as (_: any) => Promise<void>;
            const node = 'n';
            await fn(node);
            strictEqual(child.parent, node);
        });
    });

    describe('.appendComponent<T>(typer: new () => T, node: any)', () => {
        it('string', () => {
            const self = new Self(null as any, null as any, null as any, null as any);

            const node = {
                addComponent: (arg: any) => {
                    strictEqual(arg, 'str');
                    return 'child';
                }
            };
            const fn = Reflect.get(self, 'appendComponent').bind(self) as (_: any, __: any) => any;
            const res = fn('str', node);
            strictEqual(res, 'child');
        });

        it('ctor', () => {
            const self = new Self(null as any, null as any, null as any, null as any);

            const node = {
                addComponent: (arg: any) => {
                    strictEqual(arg, UITransform);
                    return 'child';
                }
            };
            ConverterBase.getCcPropFunc = (arg: any) => {
                strictEqual(arg, UITransform.name);
                return UITransform;
            };

            const fn = Reflect.get(self, 'appendComponent').bind(self) as (_: any, __: any) => any;
            const res = fn(UITransform, node);
            strictEqual(res, 'child');
        });
    });

    describe('.appendSprite(node: any)', () => {
        it('ok', async () => {
            const mockAssetService = new Mock<AssertServiceBase>();
            const mockFileFactory = new Mock<FileFactoryBase>();
            const self = new Self(mockAssetService.actual, null as any, mockFileFactory.actual, null as any);

            const mockTreePSD = new Mock<TreePSD>();
            mockTreePSD.expectReturn(
                r => r.get(enum_.Psd.name),
                'a'
            );

            const mockImagePSD = new Mock<ImagePSD>();
            mockTreePSD.expectReturn(
                r => r.get(enum_.Psd.image),
                mockImagePSD.actual
            );

            const mockFile = new Mock<IFile>({
                path: 'file-path'
            });
            mockFileFactory.expectReturn(
                r => r.buildFile(`texture/a.png`),
                mockFile.actual
            );

            mockImagePSD.expected.saveAsPng('file-path');

            ConverterBase.getCcPropFunc = (arg: any) => {
                strictEqual(arg, Sprite.name);
                return Sprite;
            };

            const sprite = {
                sizeMode: '',
                spriteFrame: null
            } as Sprite;
            const node = {
                addComponent: (arg: any) => {
                    strictEqual(arg, Sprite);
                    return sprite;
                }
            };

            mockAssetService.expectReturn(
                r => r.load(`texture/a.png`, 'spriteFrame'),
                'sf'
            );

            const fn = Reflect.get(self, 'appendSprite').bind(self) as (_: TreePSD, __: any) => Promise<Sprite>;
            const res = await fn(mockTreePSD.actual, node);
            strictEqual(res.sizeMode, Sprite.SizeMode.TRIMMED);
            strictEqual(res.spriteFrame, 'sf');
        });
    });

    describe(`createNode(opt?: Partial<{
        x: number;
        y: number;
        name: string;
    }>)`, () => {
        it('ok', () => {
            const mockTreePSD = new Mock<TreePSD>();
            const self = new Self(null as any, null as any, null as any, mockTreePSD.actual);

            const getComponentCtorFuncAssets: Function[] = [Node, Layers, Vec2, Vec3];
            ConverterBase.getCcPropFunc = (arg: any) => {
                const ctor = getComponentCtorFuncAssets.shift();
                strictEqual(arg, ctor?.name);
                return ctor;
            };

            mockTreePSD.expectReturn(
                r => r.get(enum_.Psd.name),
                'a'
            );

            Reflect.set(self, 'm_X', 1);
            Reflect.set(self, 'm_Y', 2);
            Reflect.set(self, 'createVec3', (arg: any, arg1: any) => {
                strictEqual(arg, 1);
                strictEqual(arg1, 2);
                return 3;
            });

            const transform = {
                height: null,
                width: null
            };
            Reflect.set(self, 'appendComponent', (arg: any, arg1: any) => {
                strictEqual(arg, UITransform);
                strictEqual(arg1.constructor, Node);
                return transform;
            });

            mockTreePSD.expectReturn(
                r => r.get(enum_.Psd.height),
                4
            );

            mockTreePSD.expectReturn(
                r => r.get(enum_.Psd.width),
                5
            );

            const fn = Reflect.get(self, 'createNode').bind(self) as (_?: any) => Node;
            const res = fn();
            strictEqual(res.layer, 'ui');
            strictEqual(res.name, 'a');
            strictEqual(res.position, 3);
            strictEqual(transform.height, 4);
            strictEqual(transform.width, 5);
        });
    });

    describe('.createVec3(x: number, y: number)', () => {
        it('ok', () => {
            const self = new Self(null as any, null as any, null as any, null as any);

            ConverterBase.getCcPropFunc = (arg: any) => {
                strictEqual(arg, Vec3.name);
                return Vec3;
            };

            const fn = Reflect.get(self, 'createVec3').bind(self) as (_: any, __: any) => Vec3;
            const res = fn(1, 2);
            strictEqual(res.x, 1);
            strictEqual(res.y, 2);
            strictEqual(res.z, 0);
        });
    });
});

class TT {
    public static say() {

    }
}

TT.say = console.log;